﻿#ifndef __SOCK_HELPER_H__
#define __SOCK_HELPER_H__

// Windows 下socket helper class

#ifdef _WIN32
#include <Winsock2.h>
#pragma comment(lib, "Ws2_32.lib")
#include <ws2ipdef.h>
#include <Ws2tcpip.h>

#include <Wininet.h>

#include <atlstr.h>
#ifndef __AFX_H__
using namespace ATL;
#endif
// Linux Version Inclusion
#else

#ifndef SOCKET_DEFINED
#define SOCKET_DEFINED
typedef int SOCKET;
#endif // !SOCKET_DEFINED

#ifndef INVALID_SOCKET
#define INVALID_SOCKET  (SOCKET)(~0)
#endif
#ifndef SOCKET_ERROR
#define SOCKET_ERROR            (-1)
#endif
#endif

#include <MyOutputDebugInfo.h>


class CSocketHelper
{
public:
    CSocketHelper()
    {
        //isSocketInitiated = false;
    }

    // 检测当前运行平台是否是 big endian
    // ==0， 是 little endian
    // ==(~0), 是 big endian
    static int CheckIsBigEndian()
    {
        int is_big_endian = 0;
        unsigned int c = 0x01020304;
        if (*((unsigned char*)&c) == 0x01)
            is_big_endian = -1;
        return is_big_endian;
    }

    // 本机字节序转换成网络字节序
    static unsigned long long htonll(unsigned long long host)
    {
        int is_big_endian = 0;
        //unsigned long long tmp = 0;
        unsigned long temp_low, temp_high;

        is_big_endian = CheckIsBigEndian();
        if (is_big_endian)
            return host;

        temp_low = htonl((long)host);
        temp_high = htonl((long)(host >> 32));

        host &= 0;  
        host |= temp_low;  
        host <<= 32;   
        host |= temp_high;
        return host;
    }

    // 网络字节序转换成本机字节序
    static unsigned long long ntohll(unsigned long long net)
    {
        unsigned long long host = htonll(net);
        return host;
    }

    static bool SocketStartup()
    {
        bool isSuccess = false;
#ifdef _WIN32
        int sockError = 0;
        WORD wVersionRequested;
        WSADATA wsaData;

        do
        {
            wVersionRequested = MAKEWORD(2, 2);
            sockError = WSAStartup(wVersionRequested, &wsaData);
            if (sockError != 0)
            {
                Output::OutputDebugPrintf2("WSAStartup 调用失败, 返回值=%d\r\n", sockError);
                break;
            }

            //isSocketInitiated = true;
            isSuccess = true;
        } while (0);
#endif
        return isSuccess;
    }

    static BOOL GetWinINetErrorMessage(DWORD dwErrorCode, CString& errorMessage)
    {
        BOOL bRet = FALSE;

        LPVOID lpMsgBuf = NULL;
        LPVOID lpDisplayBuf = NULL;
        DWORD dwSysCode = 0;
        DWORD dwRet = 0;

        do 
        {
            switch (dwErrorCode)
            {
            case ERROR_INTERNET_CANNOT_CONNECT:
                errorMessage.Format(_T("无法连接服务器"));
                bRet = TRUE;
                break;
            case ERROR_INTERNET_TIMEOUT:
                errorMessage.Format(_T("连接服务器超时"));
                bRet = TRUE;
                break;
            }

            if (bRet)
                break;

            bRet = FALSE;

            dwRet = FormatMessageA(
                FORMAT_MESSAGE_ALLOCATE_BUFFER | 
                FORMAT_MESSAGE_FROM_SYSTEM |
                FORMAT_MESSAGE_IGNORE_INSERTS, NULL, 
                dwErrorCode, 
                //MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), 
                MAKELANGID(LANG_CHINESE_SIMPLIFIED, SUBLANG_CHINESE_SIMPLIFIED),
                (LPSTR)&lpMsgBuf, 
                0, NULL);
            if (dwRet == 0)
            {
                errorMessage.Format(_T("不可识别错误码：%d"), dwErrorCode);
                dwSysCode = GetLastError();
                break;
            }

            CStringA tmp = (char*)lpMsgBuf;
            errorMessage = tmp;//(char*)lpMsgBuf;
            bRet = TRUE;
        } while (0);

        if (lpMsgBuf != NULL)
            LocalFree(lpMsgBuf);

        return bRet;
    }

    // 接收pLengthRecv指定长度的数据后才返回
    // 成功返回后， pIndexPos 的值是预期要接收的长度
    // 失败时，由 pSockError 带回 WSAGetLastError () 的值
    static BOOL RecvData(SOCKET sockRemote, char* pBuffer, int* pIndexPos, unsigned int* pLengthRecv, int* pSockError)
    {
        BOOL bSuccess = FALSE;
        int sockRet = 0;
#ifdef _DEBUG
        CString strMsg;
#endif // _DEBUG

        *pSockError = 0;
        do 
        {
            sockRet = recv(sockRemote, (char*)&pBuffer[*pIndexPos], *pLengthRecv, 0);
            if (sockRet == SOCKET_ERROR)
            {
                *pSockError = WSAGetLastError();
                break;
            }
            if (sockRet == 0)
            {
                // 对方连接断开，假设用这个值
                *pSockError = WSAECONNRESET; // WSAENETRESET ?
                break;
            }
#ifdef _DEBUG
            Output::OutputDebugPrintf2("本次微分段实际接收 %d 字节\r\n", sockRet);
#endif // _DEBUG
            *pIndexPos += sockRet;
            *pLengthRecv -= sockRet;

        } while (*pLengthRecv > 0);

        if (*pSockError == 0)
        {
            bSuccess = TRUE;
        }

        return bSuccess;
    }

    // 发送pLengthRecv指定长度的数据后才返回
    // 成功返回后， pIndexPos 的值是预期要发送的长度
    // 失败时，由 pSockError 带回 WSAGetLastError () 的值
    static BOOL SendData(SOCKET sockRemote, const char* pBuffer, int* pIndexPos, unsigned int* pLengthSend, int* pSockError)
    {
        BOOL bSuccess = FALSE;
        int sockRet = 0;
#ifdef _DEBUG
        CString strMsg;
#endif // _DEBUG

        *pSockError = 0;
        do 
        {
            sockRet = send(sockRemote, &pBuffer[*pIndexPos], *pLengthSend, 0);
            if (sockRet == SOCKET_ERROR)
            {
                *pSockError = WSAGetLastError();
                break;
            }

#ifdef _DEBUG
            //strMsg.Format(_T("本次微分段实际发送 %d 字节\r\n"), sockRet);
            //OutputDebugString(strMsg);
            Output::OutputDebugPrintf2("本次微分段实际发送 %d 字节\r\n", sockRet);
#endif // _DEBUG
            *pIndexPos += sockRet;
            *pLengthSend -= sockRet;

        } while (*pLengthSend > 0);

        if (*pSockError == 0)
        {
            bSuccess = TRUE;
        }

        return bSuccess;
    }

protected:
    //static bool isSocketInitiated;
private:
};

//CSocketHelper::isSocketInitiated = false;



#endif